<?php
declare (strict_types=1);

// +----------------------------------------------------------------------
// | SCApi
// +----------------------------------------------------------------------
// | 版权所有 2021~2022 贾二小 [ http://www.ipsoul.com ]
// +----------------------------------------------------------------------
// | 官方网站: http://www.ipsoul.com
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免费声明 ( http://doc.ipsoul.com/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库：https://gitee.com/jiaerxiao/scapi
// | github 代码仓库：https://github.com/jiaerxiao/scapi
// +----------------------------------------------------------------------

namespace app\common\service;

use think\App;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Db;
use think\Request;

/**
 * 公共Service
 */
class BaseService
{
    /**
     * Request实例
     * @var Request
     */
    protected $request;

    /**
     * 应用实例
     * @var App
     */
    protected $app;

    /**
     * 配置参数
     * @var
     */
    protected $config;

    /**
     * 当前Token
     * @var
     */
    protected $token;

    /**
     * 当前用户
     * @var
     */
    protected $userData;

    /**
     * 构造方法
     * @access public
     * @param App $app 应用对象
     */
    public function __construct(App $app, string $name, string $tableName)
    {
        $this->app = $app;
        $this->request = $this->app->request;
        // 初始化
        $this->initialize();
        // 更新
        $this->config['name'] = $name;
        $this->config['tableName'] = $tableName;
        // 当前Token
        $this->token = $this->request->token;
        if (!empty($this->token)) {
            // 当前用户
            $this->userData = cache($this->token);
        }
        if ($this->config['isDelete']) {
            array_push($this->config['booleans'], 'isDelete');
        }
    }

    /**
     * 初始化
     * @author 贾二小
     * @since 2021/11/2
     */
    protected function initialize()
    {
        //别名
        if (!isset($this->config['tableAlias'])) {
            $this->config['tableAlias'] = 'a';
        }
        //别名
        if (!isset($this->config['tableField'])) {
            $this->config['tableField'] = '*';
        }
        //是否分页显示
        if (!isset($this->config['tableLimit'])) {
            $this->config['tableLimit'] = true;
        }
        //是否假删除
        if (!isset($this->config['isDelete'])) {
            $this->config['isDelete'] = true;
        }
        //是否转驼峰
        if (!isset($this->config['isCamel'])) {
            $this->config['isCamel'] = true;
        }
        //int类型自动转Boolean类型（1:true、0:false）
        if (!isset($this->config['booleans'])) {
            $this->config['booleans'] = [];
        }
        //是否插入创建&更新人
        if (!isset($this->config['autoWriteUser'])) {
            $this->config['autoWriteUser'] = true;
        }
    }

    /**
     * 表格数据
     * @param array $map 查询条件
     * @param bool $none 返回空
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @author 贾二小
     * @since 2021/11/20
     */
    public function table(array $map = [], bool $none = false): array
    {
        if ($none) {
            return ['total' => 0, 'rows' => [],];
        }
        return [
            'total' => $this->count($map),
            'rows' => $this->list($map, true),
        ];
    }

    /**
     * 获取数量
     * @param array $map 搜索条件
     * @author 贾二小
     * @since 2021/11/20
     */
    public function count(array $map = []): int
    {
        // 驼峰转下划线(首字母小写)
        if ($this->config['isCamel'] && !empty($map)) {
            $map = map_to_snake($map);
        }
        $map = array_merge($map, $this->map());
        return Db::name($this->config['tableName'])
            ->alias($this->config['tableAlias'])
            ->where($map)
            ->count();
    }

    /**
     * 默认搜索条件
     * @author 贾二小
     * @since 2021/11/6
     */
    protected function map(): array
    {
        $map = [];
        if ($this->config['isDelete']) {
            $map[] = [$this->config['tableAlias'] . '.is_delete', '=', 0];
        }
        return $map;
    }

    /**
     * 获取列表
     * @param array $map 搜索条件
     * @return array|Collection|Db[]
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @author 贾二小
     * @since 2021/11/6
     */
    public function list(array $map = [], $isPage = false)
    {
        $field = $this->config['tableField'];
        // 驼峰转下划线(首字母小写)
        if ($this->config['isCamel']) {
            if (!empty($map)) {
                $map = map_to_snake($map);
            }
            if ($field != '*') {
                $field = field_to_snake($field);
            }
        }
        $map = array_merge($map, $this->map());
        $data = Db::name($this->config['tableName'])
            ->alias($this->config['tableAlias'])
            ->where($map)
            ->field($field);
        //是否分页显示
        if ($isPage) {
            //获取参数
            $paramData = $this->request->get(['page', 'pageSize']);
            $data = $data->page(intval($paramData['page']), intval($paramData['pageSize']))
                ->select()
                ->map(function ($item) {
                    // 下划线转驼峰(首字母小写)
                    if ($this->config['isCamel']) {
                        $item = to_camel($item);
                    }
                    //int类型自动转Boolean类型(1:true、0:false)
                    if (!empty($this->config['booleans'])) {
                        foreach ($this->config['booleans'] as $key) {
                            if(isset($item[$key])) $item[$key] = $item[$key] == 1;
                        }
                    }
                    return $item;
                });
        } else {
            $data = $data->select()
                ->map(function ($item) {
                    // 下划线转驼峰(首字母小写)
                    if ($this->config['isCamel']) {
                        $item = to_camel($item);
                    }
                    //int类型自动转Boolean类型(1:true、0:false)
                    if (!empty($this->config['booleans'])) {
                        foreach ($this->config['booleans'] as $key) {
                            if(isset($item[$key])) $item[$key] = $item[$key] == 1;
                        }
                    }
                    return $item;
                });
        }
        return $data;
    }

    /**
     * 根据ID获取明细
     * @param int $id 唯一ID
     * @return array|false|Db|null
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @author 贾二小
     * @since 2021/11/6
     */
    public function infoById(int $id = 0)
    {
        $map = [];
        $map[] = [$this->config['tableAlias'] . '.id', '=', $id];
        return $this->info($map);
    }

    /**
     * 获取明细
     * @param array $map 查询条件
     * @return array|false|Db|null
     * @throws DbException
     * @throws DataNotFoundException
     * @throws ModelNotFoundException
     * @author 贾二小
     * @since 2021/11/6
     */
    public function info(array $map = [])
    {
        $field = $this->config['tableField'];
        // 驼峰转下划线(首字母小写)
        if ($this->config['isCamel']) {
            if (!empty($map)) {
                $map = map_to_snake($map);
            }
            if ($field != '*') {
                $field = field_to_snake($field);
            }
        }
        $map = array_merge($map, $this->map());
        $data = Db::name($this->config['tableName'])
            ->alias($this->config['tableAlias'])
            ->where($map)
            ->field($field)
            ->find();
        // 下划线转驼峰(首字母小写)
        if ($this->config['isCamel'] && !empty($data)) {
            $data = to_camel($data);
        }
        //int类型自动转Boolean类型(1:true、0:false)
        if (!empty($this->config['booleans']) && !empty($data)) {
            foreach ($this->config['booleans'] as $key) {
                if(isset($item[$key])) $item[$key] = $item[$key] == 1;
            }
        }
        return $data;
    }

    /**
     * 根据ID更新明细
     * @param int $id 唯一ID
     * @param array $data 数据
     * @return int
     * @throws DbException
     * @author 贾二小
     * @since 2021/11/7
     */
    public function updateById(int $id, array $data): int
    {
        $map = [];
        $map[] = [$this->config['tableAlias'] . '.id', '=', $id];
        if ($this->config['autoWriteUser'] && !empty($this->userData)) {
            $data['update_user_id'] = $this->userData['id'];
        }
        // 驼峰转下划线(首字母小写)
        return $this->updateData($data, $map);
    }

    /**
     * 更新记录
     * @param array $data 明细
     * @param array $map 条件
     * @throws DbException
     * @since 2021/12/13
     * @author 贾二小
     */
    public function updateData(array $data, array $map): int
    {
        $map = array_merge($map, $this->map());
        if ($this->config['isCamel'] && !empty($data)) {
            $data = to_snake($data);
        }
        if ($this->config['autoWriteUser'] && !empty($this->userData)) {
            $data['update_user_id'] = $this->userData['id'];
        }
        return Db::name($this->config['tableName'])->alias($this->config['tableAlias'])->where($map)->update($data);
    }

    /**
     * 根据条件更新明细
     * @param array $map 条件
     * @param array $data 数据
     * @return int
     * @throws DbException
     * @author 贾二小
     * @since 2021/11/24
     */
    public function updateByMap(array $map = [], array $data = []): int
    {
        // 驼峰转下划线(首字母小写)
        if ($this->config['isCamel'] && !empty($map)) {
            $map = map_to_snake($map);
        }
        if ($this->config['autoWriteUser'] && !empty($this->userData)) {
            $data['update_user_id'] = $this->userData['id'];
        }
        // 驼峰转下划线(首字母小写)
        return $this->updateData($data, $map);
    }

    /**
     * 插入记录并获取自增ID
     * @param array $data 数据
     * @return integer|string
     * @author 贾二小
     * @since 2021/11/24
     */
    public function insertGetId(array $data)
    {
        // 驼峰转下划线(首字母小写)
        if ($this->config['isCamel'] && !empty($data)) {
            $data = to_snake($data);
        }
        if ($this->config['autoWriteUser'] && !empty($this->userData)) {
            $data['create_user_id'] = $this->userData['id'];
        }
        return Db::name($this->config['tableName'])->insertGetId($data);
    }

    /**
     * 批量插入记录
     * @param array $dataSet 数据集
     * @param integer $limit 每次写入数据限制
     * @return integer
     * @author 贾二小
     * @since 2021/12/13
     */
    public function insertAll(array $dataSet, int $limit = 100): int
    {
        // 驼峰转下划线(首字母小写)
        if ($this->config['isCamel']) {
            $dataSet = array_map(function ($item) {
                return to_snake($item);
            }, $dataSet);
        }
        return Db::name($this->config['tableName'])->insertAll($dataSet, $limit);
    }

    /**
     * 删除单条记录
     * @param int $id 唯一ID
     * @return int
     * @throws DbException
     * @author 贾二小
     * @since 2021/12/1
     */
    public function deleteById(int $id): int
    {
        $map = [];
        $map[] = ['id', '=', $id];
        return $this->deleteData($map);
    }

    /**
     * 删除记录
     * @param array $map 条件
     * @return int
     * @throws DbException
     * @author 贾二小
     * @since 2021/12/13
     */
    private function deleteData(array $map = []): int
    {
        $map = array_merge($map, $this->map());
        //假删除的情况下，必有更新人ID
        if ($this->config['isDelete']) {
            $data = [];
            $data['is_delete'] = 1;
            if ($this->config['autoWriteUser'] && !empty($this->userData)) {
                $data['update_user_id'] = $this->userData['id'];
            }
            return Db::name($this->config['tableName'])->alias($this->config['tableAlias'])->where($map)->update($data);
        } else {
            return Db::name($this->config['tableName'])->where($map)->delete();
        }
    }

    /**
     * 批量删除多条记录
     * @param array $ids 唯一ID数组
     * @return int
     * @throws DbException
     * @author 贾二小
     * @since 2021/12/1
     */
    public function deleteByIds(array $ids): int
    {
        $map = [];
        $map[] = ['id', 'in', $ids];
        return $this->deleteData($map);
    }

    /**
     * 删除记录
     * @param array $map 条件
     * @return int
     * @throws DbException
     * @author 贾二小
     * @since 2021/12/13
     */
    public function deleteByMap(array $map): int
    {
        $map = map_to_snake($map);
        return $this->deleteData($map);
    }

    /**
     * 原生SQL查询
     * @param string $sql SQL
     * @param array $paramData 参数
     * @author 贾二小
     * @since 2021/12/26
     */
    public function query(string $sql, array $paramData = [])
    {
        $data = Db::query($sql, $paramData);
        return array_map(function ($item) {
            // 下划线转驼峰(首字母小写)
            if ($this->config['isCamel']) {
                $item = to_camel($item);
            }
            //int类型自动转Boolean类型(1:true、0:false)
            if (!empty($this->config['booleans'])) {
                foreach ($this->config['booleans'] as $key) {
                    if(isset($item[$key])) $item[$key] = $item[$key] == 1;
                }
            }
            return $item;
        }, $data);
    }

    /**
     * 原生SQL更新和写入
     * @param string $sql SQL
     * @param array $paramData 参数
     * @author 贾二小
     * @since 2021/12/26
     */
    public function execute(string $sql, array $paramData = [])
    {
        return Db::execute($sql, $paramData);
    }

    /**
     * 执行数据库事务
     * @access public
     * @param callable $callback 数据操作方法回调
     * @return mixed
     * @author 贾二小
     * @since 2022/2/20
     */
    public function transaction(callable $callback)
    {
        return Db::transaction($callback);
    }
}